package com.xinzhitong.www.utils;

import com.aliyuncs.CommonRequest;
import com.aliyuncs.CommonResponse;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile;
import com.xinzhitong.www.exception.CheckCodeException;
import com.xinzhitong.www.exception.PhoneException;
import com.xinzhitong.www.exception.checkcode.CheckCodeInvalidException;
import com.xinzhitong.www.exception.phone.ContainsOtherCharacterException;
import com.xinzhitong.www.exception.phone.PhoneLengthNotStandardException;
import lombok.Data;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.ehcache.EhCacheManager;

import java.util.*;

/**
 * 验证码短信工具类
 *
 * @author 徐鑫
 */
@Data
public class SMSUtil {

    /**
     * 验证码短信类型枚举类
     *
     * @author 徐鑫
     */
    public enum SMSCodeType {
        /**
         * 登录
         */
        LOGIN,
        /**
         * 注册
         */
        REGISTER,
        /**
         * 重置密码
         */
        RESETPASSWORD
    }

    /**
     * properties文件相对路径
     */
    private final static String PROPERTIESPATH = "SMS.properties";

    /**
     * 缓存管理器
     */
    private static EhCacheManager cacheManager;

    /**
     * 登录验证码缓存
     */
    private static Cache<String, String> loginCache;

    /**
     * 注册验证码缓存
     */
    private static Cache<String, String> registerCache;

    /**
     * 重置密码验证码缓存
     */
    private static Cache<String, String> resetPasswordCache;

    /**
     * <验证码功能-templateCode>
     */
    private static Map<String, String> templateCode;

    /**
     * 验证码池
     */
    private static String[] checkCodePool;

    /**
     * 随机数生成器
     */
    private static Random random = new Random();

    static {
        //初始化缓存管理器
        cacheManager = new EhCacheManager();
        cacheManager.setCacheManagerConfigFile("classpath:SMS-ehcache-setting.xml");

        //分别设置登录、注册、重置密码的缓存
        loginCache = cacheManager.getCache("loginCache");
        registerCache = cacheManager.getCache("registerCache");
        resetPasswordCache = cacheManager.getCache("resetPasswordCache");

        //设置登录、注册、重置密码的templateCode
        templateCode = new HashMap<>();
        templateCode.put("LOGIN", "SMS_170055543");
        templateCode.put("REGISTER", "SMS_170346204");
        templateCode.put("RESETPASSWORD", "SMS_170346207");

        //初始化验证码池
        List<String> checkCodesList = new ArrayList<>();
        for (int i = 1000; i < 1000000; i++) {
            StringBuilder tmpBuilder = new StringBuilder(i + "");
            while (tmpBuilder.length() != 6)
                tmpBuilder.insert(0, "0");
            String tmp = tmpBuilder.toString();
            if (!tmp.matches(".*[0]{3,}|[1]{3,}[2]{3,}[3]{3,}|[4]{3,}|[5]{3,}|[6]{3,}|[7]{3,}|[8]{3,}|[9]{3,}.*"))
                checkCodesList.add(tmp);
        }
        checkCodePool = checkCodesList.toArray(new String[]{});
        //System.out.println(Arrays.toString(checkCodePool));
    }

    /**
     * 发送短信验证码
     *
     * @param phone       目标电话号码
     * @param smsCodeType 短信验证码类型
     */
    public static void sendSMSCode(String phone, SMSCodeType smsCodeType) throws PhoneException {
        checkPhone(phone);
        SMSConfig smsConfig = new SMSConfig();
        PropertiesHelper.getFromPropertiesFile(PROPERTIESPATH, smsConfig);
        DefaultProfile profile =
                DefaultProfile.getProfile(
                        smsConfig.getRegionId(),
                        smsConfig.getAccessKeyId(),
                        smsConfig.getSecret()
                );
        IAcsClient client = new DefaultAcsClient(profile);

        CommonRequest request = new CommonRequest();
        request.setMethod(MethodType.POST);
        request.setDomain(smsConfig.getDomain());
        request.setVersion(smsConfig.getVersion());
        request.setAction(smsConfig.getAction());
        request.putQueryParameter("PhoneNumbers", phone);
        request.putQueryParameter("SignName", smsConfig.getSignName());
        request.putQueryParameter("TemplateCode", templateCode.get(smsCodeType.toString()));

        //当缓存中存在相同验证码时不停地进行生成
        String checkCode;
        do {
            checkCode = getCheckCode();
        } while (loginCache.values().contains(checkCode));
        request.putQueryParameter("TemplateParam", "{\"code\" : \"" + checkCode + "\"}");
        System.out.println("发送给手机用户：\"" + phone + "\"的验证码已生成");

        //将验证码存入缓存中
        switch (smsCodeType) {
            case LOGIN:
                loginCache.put(phone, checkCode);
                break;
            case REGISTER:
                registerCache.put(phone, checkCode);
                break;
            case RESETPASSWORD:
                resetPasswordCache.put(phone, checkCode);
                break;
        }
        try {
            CommonResponse response = client.getCommonResponse(request);
            System.out.println(response.getData());
            System.out.println("给手机用户：\"" + phone + "\"的验证码已发送");
        } catch (ClientException e) {
            e.printStackTrace();
        }
    }

    /**
     * 验证验证码正确
     *
     * @param phone       用户名
     * @param code        用户所给的验证码
     * @param smsCodeType 验证码类型（登录、注册、重置密码）
     * @return 是否正确
     */
    public static boolean wrongSMSCode(String phone, String code, SMSCodeType smsCodeType) throws CheckCodeException {
        checkPhone(phone);
        try {
            switch (smsCodeType) {
                case LOGIN:
                    if (loginCache.get(phone).equals(code)) {
                        loginCache.remove(phone);
                        return false;
                    }
                    break;
                case REGISTER:
                    if (registerCache.get(phone).equals(code)) {
                        registerCache.remove(phone);
                        return false;
                    }
                    break;
                case RESETPASSWORD:
                    if (resetPasswordCache.get(phone).equals(code)) {
                        resetPasswordCache.remove(phone);
                        return false;
                    }
                    break;
            }
        } catch (NullPointerException e) {
            e.printStackTrace();
            throw new CheckCodeInvalidException();
        }
        return true;
    }

    /**
     * 生成验证码
     *
     * @return 6位验证码
     */
    private static String getCheckCode() {
        return checkCodePool[random.nextInt(checkCodePool.length)];
    }

    /**
     * 检查电话号码是否规范
     *
     * @param phone 电话号码
     */
    private static void checkPhone(String phone) {
        if (phone.length() != 11)
            throw new PhoneLengthNotStandardException();
        if (!phone.matches("^[0-9]+$"))
            throw new ContainsOtherCharacterException();
    }
}

/**
 * SMS配置类
 *
 * @author 徐鑫
 */
@Data
class SMSConfig {

    /**
     * 区域id
     */
    private String regionId;

    /**
     * 阿里云密钥id
     */
    private String accessKeyId;

    /**
     * 密码
     */
    private String secret;

    /**
     * 领域
     */
    private String domain;

    /**
     * 版本
     */
    private String version;

    /**
     * 操作
     */
    private String action;

    /**
     * 签名名称
     */
    private String SignName;

}
